package cn.fotoit.product.util;

import org.springframework.util.StringUtils;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Transient;
import javax.persistence.Version;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;


/**
 * spring data 反向工具
 * 注意：entity包下的类必须有 @Id才能创建repository
 * entity包下的属性 @Column(nullable = false) 与controller的@RequestParam对应
 * 实体的createTime   updateTime  名字必须一致
 * id 必须为Integer 类型，不是Integer类型的生成的service和controller需要轻微调整
 */
public class SpringDataGenerate {

    private static String rootUrl = new File("").getAbsolutePath() + "/src/main/java/";

    public static void main(String[] args) throws Exception {
        //项目根路径
        //模块包路径
        String modulesPackage = "cn.fotoit.product.app1";
        List<Class> classes = ClassUtil.doScan(modulesPackage + ".entity");
        generateRepository(classes, modulesPackage);
        //缓存名前缀
        String cacheNamesPrefix = "queue_management";
        generateService(classes, modulesPackage, cacheNamesPrefix);
        generateController(classes, modulesPackage);
    }

    //构建controller
    private static void generateController(List<Class> classes, String modulesPackage) throws IOException {
        String controllerPackage = modulesPackage + ".controller";
        String url = controllerPackage.replaceAll("\\.", "/");
        File file = new File(rootUrl + url);
        if (file.exists()) {
        } else {
            file.mkdir();
        }
        for (int i = 0; i < classes.size(); i++) {
            String simpleName = classes.get(i).getSimpleName();
            FileWriter fileWriter = new FileWriter(rootUrl + url + "/" + simpleName + "Controller.java");
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("package " + controllerPackage + ";\n");
            stringBuffer.append("import " + modulesPackage + ".entity." + simpleName + ";\n");
            stringBuffer.append("import " + modulesPackage + ".service." + simpleName + "Service" + ";\n");
            stringBuffer.append("import cn.fotoit.product.util.ReturnBean;\n" +
                    "import org.springframework.beans.factory.annotation.Autowired;\n" +
                    "import org.springframework.web.bind.annotation.*;\n\n\n");
            stringBuffer.append("@RestController\n" +
                    "@CrossOrigin\n" +
                    "@RequestMapping(\"" + StringUtils.uncapitalize(simpleName) + "\")\n" +
                    "public class " + simpleName + "Controller {\n\n" +
                    "    @Autowired\n" +
                    "    private " + simpleName + "Service " + StringUtils.uncapitalize(simpleName) + "Service;\n\n");

            //count ==0  添加方法  count ==1 修改方法
            for (int count = 0; count < 2; count++) {
                stringBuffer.append("    @PostMapping(\"" + (count == 0 ? "add" : "updateNotNull") + "\")\n" +
                        "    public ReturnBean " + (count == 0 ? "add" : "updateNotNull") + "(\n");
                Field[] declaredFields = classes.get(i).getDeclaredFields();
                StringBuffer argStr = new StringBuffer();
                StringBuffer newObjStr = new StringBuffer();
                newObjStr.append("        " + simpleName + " " + StringUtils.uncapitalize(simpleName) + " = new " + simpleName + "();\n");

                if (count != 0) {
                    newObjStr.append("        " + StringUtils.uncapitalize(simpleName) + ".setId(id);\n");
                    stringBuffer.append("            @RequestParam Integer id");
                    for (int i1 = 0; i1 < declaredFields.length; i1++) {
                        Field declaredField = declaredFields[i1];
                        if (declaredField.getName().equals("createTime") || declaredField.getName().equals("updateTime")) {
                            continue;
                        }
                        if (declaredField.getAnnotation(Id.class) == null) {
                            argStr.append(",\n            " + declaredField.getType().getSimpleName() + " " + declaredField.getName());
                            newObjStr.append("        " + StringUtils.uncapitalize(simpleName) + ".set" + StringUtils.capitalize(declaredField.getName()) + "(" + declaredField.getName() + ");\n");
                        }
                    }
                } else {
                    for (int i1 = 0; i1 < declaredFields.length; i1++) {
                        Field declaredField = declaredFields[i1];
                        if (declaredField.getName().equals("createTime") || declaredField.getName().equals("updateTime")) {
                            continue;
                        }
                        if (declaredField.getAnnotation(Id.class) == null && declaredField.getAnnotation(Transient.class) == null && declaredField.getAnnotation(Version.class) == null) {
                            Column annotation = declaredField.getAnnotation(Column.class);
                            if (annotation != null && !annotation.nullable()) {
                                argStr.append(",\n            @RequestParam " + declaredField.getType().getSimpleName() + " " + declaredField.getName());
                            } else {
                                argStr.append(",\n            " + declaredField.getType().getSimpleName() + " " + declaredField.getName());
                            }
                            newObjStr.append("        " + StringUtils.uncapitalize(simpleName) + ".set" + StringUtils.capitalize(declaredField.getName()) + "(" + declaredField.getName() + ");\n");
                        }
                    }
                }

                stringBuffer.append(count == 0 ? argStr.substring(2) : argStr.substring(0));
                stringBuffer.append(") {\n");
                stringBuffer.append(newObjStr);
                stringBuffer.append("        " + StringUtils.uncapitalize(simpleName) + "Service." + (count == 0 ? "add" : "updateNotNull") + "(" + StringUtils.uncapitalize(simpleName) + ");\n" +
                        "        return ReturnBean.success(\"" + (count == 0 ? "添加" : "修改") + "成功\");\n" +
                        "    }\n\n");
            }

            stringBuffer.append("    @PostMapping(\"deleteById\")\n" +
                    "    public ReturnBean deleteById(@RequestParam Integer id) {\n" +
                    "        " + StringUtils.uncapitalize(simpleName) + "Service.deleteById(id);\n" +
                    "        return ReturnBean.success(\"删除成功\");\n" +
                    "    }\n\n" +

                    "    @PostMapping(\"findAll\")\n" +
                    "    public ReturnBean findAll(\n" +
                    "            @RequestParam(required = false, defaultValue = \"1\") Integer page,\n" +
                    "            @RequestParam(required = false, defaultValue = \"15\") Integer size) {\n" +
                    "        return ReturnBean.success(\"查询成功\", " + StringUtils.uncapitalize(simpleName) + "Service.findAll(page, size));\n" +
                    "    }\n\n" +

                    "    @PostMapping(\"findById\")\n" +
                    "    public ReturnBean findById(\n" +
                    "            @RequestParam Integer id) {\n" +
                    "        return ReturnBean.success(\"查询成功\", " + StringUtils.uncapitalize(simpleName) + "Service.findById(id));\n" +
                    "    }\n\n\n" +
                    "}\n");
            fileWriter.write(stringBuffer.toString());//向文件里面写入字符串,会自动覆盖原内容
            fileWriter.close();
        }
    }


    //生成service
    private static void generateService(List<Class> classes, String modulesPackage, String
            cacheNamesPrefix) throws IOException {
        String entityPackage = modulesPackage + ".service";
        String url = entityPackage.replaceAll("\\.", "/");
        File file = new File(rootUrl + url);
        if (file.exists()) {
        } else {
            file.mkdir();
        }
        for (int i = 0; i < classes.size(); i++) {
            String simpleName = classes.get(i).getSimpleName();
            FileWriter fileWriter = new FileWriter(rootUrl + url + "/" + simpleName + "Service.java");
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("package " + entityPackage + ";\n");
            stringBuffer.append("import " + modulesPackage + ".entity." + simpleName + ";\n");
            stringBuffer.append("import " + modulesPackage + ".repository." + simpleName + "Repository;\n");
            stringBuffer.append("import cn.fotoit.product.util.BeanUtil;\n" +
                    "import org.springframework.beans.factory.annotation.Autowired;\n" +
                    "import org.springframework.cache.annotation.CacheConfig;\n" +
                    "import org.springframework.cache.annotation.CacheEvict;\n" +
                    "import org.springframework.cache.annotation.Cacheable;\n" +
                    "import org.springframework.data.domain.Page;\n" +
                    "import org.springframework.data.domain.PageRequest;\n" +
                    "import org.springframework.data.domain.Pageable;\n" +
                    "import org.springframework.data.domain.Sort;\n" +
                    "import org.springframework.stereotype.Service;\n" +
                    "import org.springframework.transaction.annotation.Transactional;\n" +
                    "import java.util.Optional;\n\n\n");
            stringBuffer.append("@Service\n" +
                    "@CacheConfig(cacheNames = \"" + cacheNamesPrefix + "_" + StringUtils.uncapitalize(simpleName) + "Service\")\n" +
                    "@Transactional(readOnly = true)\n" +
                    "public class " + simpleName + "Service {\n" +
                    "    @Autowired\n" +
                    "    private " + simpleName + "Repository " + StringUtils.uncapitalize(simpleName) + "Repository;\n\n" +

                    "    @CacheEvict(allEntries = true)\n" +
                    "    @Transactional\n" +
                    "    public void add(" + simpleName + " " + StringUtils.uncapitalize(simpleName) + ") {\n" +
                    "        " + StringUtils.uncapitalize(simpleName) + "Repository.save(" + StringUtils.uncapitalize(simpleName) + ");\n" +
                    "    }\n\n" +


                    "    @CacheEvict(allEntries = true)\n" +
                    "    @Transactional\n" +
                    "    public void deleteById(Integer id) {\n" +
                    "        " + StringUtils.uncapitalize(simpleName) + "Repository.deleteById(id);\n" +
                    "    }\n\n" +


                    "    @CacheEvict(allEntries = true)\n" +
                    "    @Transactional\n" +
                    "    public void updateNotNull(" + simpleName + " " + StringUtils.uncapitalize(simpleName) + ") {\n" +
                    "        Optional<" + simpleName + "> byId = " + StringUtils.uncapitalize(simpleName) + "Repository.findById(" + StringUtils.uncapitalize(simpleName) + ".getId());\n" +
                    "        if (byId.isPresent()) {\n" +
                    "            " + simpleName + " obj = byId.get();\n" +
                    "            BeanUtil.copyPropertiesIgnoreNull(" + StringUtils.uncapitalize(simpleName) + ", obj);\n" +
                    "            " + StringUtils.uncapitalize(simpleName) + "Repository.save(obj);\n" +
                    "        }\n" +
                    "    }\n\n" +

                    "    @Cacheable(key = \"'findAll'+#page+'size'+#size\")\n" +
                    "    public Page<" + simpleName + "> findAll(Integer page, Integer size) {\n" +
                    "        Sort sort = new Sort(Sort.Direction.DESC, \"createTime\");\n" +
                    "        Pageable pageable = PageRequest.of(page - 1, size, sort);\n" +
                    "        return " + StringUtils.uncapitalize(simpleName) + "Repository.findAll(pageable);\n" +
                    "    }\n\n" +
                    "" +

                    "    @Cacheable(key = \"'findById'+#id\")\n" +
                    "    public " + simpleName + " findById(Integer id) {\n" +
                    "        Optional<" + simpleName + "> byId = " + StringUtils.uncapitalize(simpleName) + "Repository.findById(id);\n" +
                    "        if (byId.isPresent()) {\n" +
                    "            return byId.get();\n" +
                    "        }\n" +
                    "        return null;\n" +
                    "    }\n\n" +
                    "}");
            fileWriter.write(stringBuffer.toString());//向文件里面写入字符串,会自动覆盖原内容
            fileWriter.close();
        }
    }

    //生成Repository
    private static void generateRepository(List<Class> classes, String modulesPackage) throws IOException {
        String repositoryPackage = modulesPackage + ".repository";
        String url = repositoryPackage.replaceAll("\\.", "/");
        File file = new File(rootUrl + url);
        if (file.exists()) {
        } else {
            file.mkdir();
        }
        for (int i = 0; i < classes.size(); i++) {

            Field[] fields = classes.get(i).getDeclaredFields();
            for (Field field : fields) {
                if (field.getAnnotation(Id.class) != null) {
                    String simpleName = classes.get(i).getSimpleName();
                    FileWriter fileWriter = new FileWriter(rootUrl + url + "/" + simpleName + "Repository.java");
                    StringBuffer stringBuffer = new StringBuffer();
                    stringBuffer.append("package " + repositoryPackage + ";\n");
                    stringBuffer.append("import " + modulesPackage + ".entity." + simpleName + ";\n");
                    stringBuffer.append(
                            "import org.springframework.data.jpa.repository.JpaRepository;\n" +
                                    "import org.springframework.stereotype.Repository;\n\n\n");
                    stringBuffer.append("@Repository\n" +
                            "public interface " + simpleName + "Repository extends JpaRepository<" + simpleName + ", " + field.getType().getSimpleName() + "> {\n" +
                            "\n" +
                            "\n" +
                            "}");
                    fileWriter.write(stringBuffer.toString());//向文件里面写入字符串,会自动覆盖原内容
                    fileWriter.close();
                    break;
                }
            }

        }
    }


}
